After a while of searching the internet, I found this piece of code that triggers upon a LINQ-based change to the Database. It trigger only once and doesn't mention or show what was changed/deleted/added, or what table was CRUDed.
static class GlobalNotifications
{
public static event OnChangeEventHandler OnChange;
public static void InitializeNotifications(string connectString)
{
// Initialize notifications
SqlDependency.Start(connectString);
// Create and register a new dependency
SqlDependency dependency = new SqlDependency();
dependency.OnChange += new OnChangeEventHandler(NotificationCallback);
System.Runtime.Remoting.Messaging.CallContext.SetData("MS.SqlDependencyCookie", dependency.Id);
}
internal static void NotificationCallback(object o, SqlNotificationEventArgs args)
{
OnChange.Invoke(o, args);
}
}
This is how I'm using it:
public partial class Nawa : Form
{
public Nawa()
{
InitializeComponent();
}
private void Nawa_Load(object sender, EventArgs e)
{
GlobalNotifications.InitializeNotifications("Server=GENISYSSERVER; Trusted_Connection=no;database=Maple_DBv1; user id=sa; password=Wc123Wc123");
GlobalNotifications.OnChange += new System.Data.SqlClient.OnChangeEventHandler(GlobalNotifications_OnChange);
}
void GlobalNotifications_OnChange(object sender, System.Data.SqlClient.SqlNotificationEventArgs e)
{
MessageBox.Show("Test");
}
private void button1_Click(object sender, EventArgs e)
{
using (DataClasses1DataContext dbcontext = new DataClasses1DataContext("Server=GENISYSSERVER; Trusted_Connection=no;database=Maple_DBv1; user id=sa; password=Wc123Wc123")) {
OrderFood random = dbcontext.OrderFoods.FirstOrDefault(id => id.ID == 10);
if (random != null) {
if (random.MenuID == 4)
random.MenuID = 1;
else
random.MenuID = 4;
dbcontext.SubmitChanges();
}
}
}
}
Can someone help in this regard? How to get more details of what was changed, type of change, Table(s) changed, and why does it fire only once. Also, how can it understand LINQ changes only? It doesn't trigger on direct changes etc.
Reference:
Extemporaneous Mumblings
A SQLDependency will only fire once, you need to recreate the dependency after it has fired.
The OnChange event fires once and then gets consumed, so you need to hook up the event again after it fires.
Dan Miser - SqlDependency
I don't have any C# code to hand to demonstrate how to deal with this but I'm sure some VB.NET should work well enough for you to come up with your own solution.
Private _permissionsDependency As SqlDependency
Private Sub doSubscribe()
_permissionsDependency = New SqlDependency(cmd.InnerCommand)
RemoveHandler _permissionsDependency.OnChange, AddressOf User_OnChange
AddHandler _permissionsDependency.OnChange, AddressOf User_OnChange
End Sub
Private Sub User_OnChange(ByVal sender As Object, ByVal e As System.Data.SqlClient.SqlNotificationEventArgs)
If _permissionsDependency IsNot Nothing Then RemoveHandler _permissionsDependency .OnChange, AddressOf User_OnChange
Select Case e.Info
Case SqlNotificationInfo.Delete
RaiseEvent UserDeleted(Me)
Case SqlNotificationInfo.Update
populateUser()
RaiseEvent UserUpdated(Me)
Case Else
End Select
End Sub
As you can see you can find out what happened by looking at e.Info, which will allow for you to know what happened (in my example I'm only looking for deletes and updates).
Related
I have a code that has a text box and a button, when i press the button, the code update the text box and after words sleep for 5 seconds, in those seconds the ui is freezing
I tried with delegates, and threading but nothing works
This is the code with delegates:
MainWindow.vb
Class MainWindow
Delegate Sub MySubDelegate(ByVal x As String)
Private m_engine As Engine
Public Sub New()
m_engine = New Engine(AddressOf WriteToLog)
InitializeComponent()
Me.DataContext = m_engine.GetViewModel()
End Sub
Public Sub WriteToLog(str As String)
Dim vv As ViewModel = CType(DataContext, ViewModel)
vv.Log = str
End Sub
Private Sub clicked(sender As Object, e As RoutedEventArgs)
m_engine.TimingRecord()
End Sub
End Class
Engine.vb
Private m_viewModel As New ViewModel
Private _msd As MainWindow.MySubDelegate
Public Sub New()
End Sub
Sub New(msd As MainWindow.MySubDelegate)
_msd = msd
End Sub
Public Function GetViewModel() As ViewModel
Return m_viewModel
End Function
Public Sub TimingRecord()
_msd("aaaaa")
SetText()
End Sub
Public Sub SetText()
Thread.Sleep(5000)
End Sub
viewModel.vb
Public Class ViewModel
Implements INotifyPropertyChanged
Private m_log As String
Public Property Log As String
Get
Return m_log
End Get
Set(value As String)
m_log = value
End Set
End Property
Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
Private Sub NotifyPropertyChanged(Optional ByVal propertyName As String = Nothing)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class
This is the code with thread:
MainWindow.vb
Delegate Sub MySubDelegate(ByVal x As String)
Private m_engine As Engine
Public Sub New()
m_engine = New Engine(AddressOf WriteToLog)
InitializeComponent()
Me.DataContext = m_engine.GetViewModel()
End Sub
Public Sub WriteToLog(str As String)
Me.Dispatcher.Invoke(Sub() CType(DataContext, ViewModel).Log = str)
'Dim vv As ViewModel = CType(DataContext, ViewModel)
'vv.Log = str
End Sub
Private Sub clicked(sender As Object, e As RoutedEventArgs)
m_engine.TimingRecord()
End Sub
Engine.vb
Public Class Engine
Private m_viewModel As New ViewModel
Private m_thread As Thread
Private _msd As MainWindow.MySubDelegate
Public Sub New(msd As MainWindow.MySubDelegate)
_msd = msd
End Sub
Public Function GetViewModel() As ViewModel
Return m_viewModel
End Function
Public Sub TimingRecord()
m_thread = New Thread(AddressOf DoRecordThread)
m_thread.IsBackground = True
m_thread.Start("aa")
SetText()
End Sub
Public Sub SetText()
Thread.Sleep(5000)
End Sub
Private Sub DoRecordThread(str As String)
_msd(str)
'm_viewModel.Log = str
End Sub
End Class
The ViewModel Stay The same. I'm using wpf so i bind the Log into the textbox and with simple code it works.
Thank you all in advance.
Thread.Sleep causing the issue. It blocks the UI thread that makes the UI frozen.
Have the long running task in different thread and once completed Update the UI(TextBox) with Application.Current.Dispatcher.
Sample Code to update the UI from background thread with Dispatcher :
Application.Current.Dispatcher.Invoke(new Action(() => { Textbox1.Text = result; }))
This is not a right way but I hope this is what you are looking for.
Update
Also, there is another way, you can use builtin BackgroundWorker.
//Add the namespace
using System.ComponentModel;
//Declare
private readonly BackgroundWorker worker = new BackgroundWorker();
//Initialize
worker.DoWork += worker_DoWork;
worker.ProgressChanged += worker_ProgressChanged;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
//method declaration
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
worker.ReportProgress(0, "Process started");
// run all background tasks here
}
private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//Progress update with e.UserState.ToString();
}
private void worker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
//update ui once worker complete his work
}
//Invoke
worker.RunWorkerAsync();
The problem solved by put the thread.sleep but very foolish of me in the viewmodel class i accidently delete the property changed now it is working perfectly thank you all for helping
I found this very interessting Code here. I tried to translate it to VB.NET, but I am not able to. I want to remove all Handlers for the event 'click' of a known button.
Can anybody help me and translate it to VB.NET?
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
button1.Click += button1_Click;
button1.Click += button1_Click2;
button2.Click += button2_Click;
}
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("Hello");
}
private void button1_Click2(object sender, EventArgs e)
{
MessageBox.Show("World");
}
private void button2_Click(object sender, EventArgs e)
{
RemoveClickEvent(button1);
}
private void RemoveClickEvent(Button b)
{
FieldInfo f1 = typeof(Control).GetField("EventClick",
BindingFlags.Static | BindingFlags.NonPublic);
object obj = f1.GetValue(b);
PropertyInfo pi = b.GetType().GetProperty("Events",
BindingFlags.NonPublic | BindingFlags.Instance);
EventHandlerList list = (EventHandlerList)pi.GetValue(b, null);
list.RemoveHandler(obj, list[obj]);
}
}
}
This is a direct translation into VB syntax, as per the OPs request.
Imports System.Reflection ' Required if not already in your code
Imports System.ComponentModel
Partial Public Class Form1
Inherits Form
Public Sub New()
InitializeComponent()
AddHandler button1.Click, AddressOf button1_Click
AddHandler button1.Click, AddressOf button1_Click2
AddHandler button2.Click, AddressOf button2_Click
End Sub
Private Sub button1_Click(sender As Object, e As EventArgs)
MessageBox.Show("Hello")
End Sub
Private Sub button1_Click2(sender As Object, e As EventArgs)
MessageBox.Show("World")
End Sub
Private Sub button2_Click(sender As Object, e As EventArgs)
RemoveClickEvent(button1)
End Sub
Private Sub RemoveClickEvent(b As Button)
Dim f1 As FieldInfo = GetType(Control).GetField("EventClick", BindingFlags.Static Or BindingFlags.NonPublic)
Dim obj As Object = f1.GetValue(b)
Dim pi As PropertyInfo = b.GetType().GetProperty("Events", BindingFlags.NonPublic Or BindingFlags.Instance)
Dim list As EventHandlerList = DirectCast(pi.GetValue(b, Nothing), EventHandlerList)
list.RemoveHandler(obj, list(obj))
End Sub
End Class
It works fine, provided of course the form has button1 and button2 dropped onto it.
The thing that makes people reject your question (aside from #phaedra 's perfectly valid comments) is that there is little point in this code. The function RemoveHandler can be used instead.
I'm developing an application and I'm trying to detect when the workstation gets locked, for example by the user pressing the Windows + L keys.
I know that the lock event has the value
WTS_SESSION_LOCK 0x7
But i don't know how to use it. I've searched the web but found nothing.
You should use the SystemEvents class in the Microsoft.Win32 namespace, especially the SystemEvents.SessionSwitch event.
SystemEvents.SessionSwitch += SystemEvents_SessionSwitch; // Subscribe to the SessionSwitch event
static void SystemEvents_SessionSwitch(object sender, SessionSwitchEventArgs e)
{
if (e.Reason == SessionSwitchReason.SessionLock)
// Add your session lock "handling" code here
}
Update
If you need to have this event activated from the program startup in a Winforms application:
static class Program
{
[STAThread]
private static void Main()
{
SystemEvents.SessionSwitch += SystemEvents_SessionSwitch; // Subscribe to the SessionSwitch event
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
static void SystemEvents_SessionSwitch(object sender, SessionSwitchEventArgs e)
{
if (e.Reason == SessionSwitchReason.SessionLock)
// Add your session lock "handling" code here
}
}
Finnallly managed to do it on VB :D
First you need to import the libs:
Imports System
Imports Microsoft.Win32
Imports System.Windows.Forms
Then you add the handler:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
AddHandler SystemEvents.SessionSwitch, AddressOf SessionSwitch_Event
End Sub
Finnally you create the sub that captures it:
Private Sub SessionSwitch_Event(ByVal sender As Object, ByVal e As SessionSwitchEventArgs)
If e.Reason = SessionSwitchReason.SessionLock Then
MsgBox("Locked")
End If
If e.Reason = SessionSwitchReason.SessionUnlock Then
MsgBox("Unlocked")
End If
End Sub
Last you remove the handler:
Private Sub closing_event(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
RemoveHandler SystemEvents.SessionSwitch, AddressOf SessionSwitch_Event
End Sub
I am working on WPF LOB application and Using Prism and delegate commands to separate UI from View Model.
When ever user make a change on a particular cell FROM UI (not from View Model or Service), I need to invoke some other functionality.
I have created the Attached Behavior
public static class DataGridCellEditEndingBehaviour
{
private static readonly DependencyProperty CellEditEndingProperty
= DependencyProperty.RegisterAttached(
"CellEditEnding",
typeof(CellEditEnding),
typeof(DataGridCellEditEndingBehaviour),
null);
public static readonly DependencyProperty CommandProperty
= DependencyProperty.RegisterAttached(
"Command",
typeof(ICommand),
typeof(DataGridCellEditEndingBehaviour),
new PropertyMetadata(OnSetCommandCallback));
public static readonly DependencyProperty CommandParameterProperty
= DependencyProperty.RegisterAttached(
"CommandParameter",
typeof(object),
typeof(DataGridCellEditEndingBehaviour),
new PropertyMetadata(OnSetCommandParameterCallback));
public static ICommand GetCommand(DataGrid control)
{
return control.GetValue(CommandProperty) as ICommand;
}
public static void SetCommand(DataGrid control, ICommand command)
{
control.SetValue(CommandProperty, command);
}
public static void SetCommandParameter(DataGrid control, object parameter)
{
control.SetValue(CommandParameterProperty, parameter);
}
public static object GetCommandParameter(DataGrid control)
{
return control.GetValue(CommandParameterProperty);
}
private static void OnSetCommandCallback
(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
DataGrid control = dependencyObject as DataGrid;
if (control != null)
{
CellEditEnding behavior = GetOrCreateBehavior(control);
behavior.Command = e.NewValue as ICommand;
}
}
private static void OnSetCommandParameterCallback
(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
DataGrid control = dependencyObject as DataGrid;
if (control != null)
{
CellEditEnding behavior = GetOrCreateBehavior(control);
behavior.CommandParameter = e.NewValue;
}
}
private static CellEditEnding GetOrCreateBehavior(DataGrid control)
{
CellEditEnding behavior =
control.GetValue(CellEditEndingProperty) as CellEditEnding;
if (behavior == null)
{
behavior = new CellEditEnding(control);
control.SetValue(CellEditEndingProperty, behavior);
}
return behavior;
}
}
public class CellEditEnding : CommandBehaviorBase<DataGrid>
{
public CellEditEnding(DataGrid control)
: base(control)
{
control.CellEditEnding += OnCellEditEnding;
}
private void OnCellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
ExecuteCommand();
}
}
And I am able to invoke the same using
local:DataGridCellEditEndingBehaviour.Command ="{Binding CellChangedCommand}"
When the event gets invoked, I don't get any eventargs in my delegateCommand in VM, how I can retrieve the event args, can I set it through Command Parameters? If so, how can i pass the event args to delegate command?
During the CellEditEndigEvent, the value is not yet stored in to the VM as it is still in transition, is there a way I can force it to happen from the handler, so I don't need to read values from CellEditEndingEventArgs, instead I can read from VM directly?
It's an attached property. You use it like
local:DataGridCellEditEndingBehaviour.CommandParameter="{Binding against whatever you want to pass}"
You have probably implement custom DataGrid which has custom property that indicates Cell that was edited or something along the lines.
I came across this trying to solve a similar problem - in a MVVM app, we have a UserControl with a DataGrid and so we need to have the RowEditEnding event bind to a Command. I could not quite follow the example above, and could not determine how to find CommandBehaviorBase.
Partly based on the answer at MvvmLight EventToCommand and WPFToolkit DataGrid double-click, I implemented our AttachedBehaviour as follows:
Public Class DataGridHelper
Public Shared ReadOnly RowEditEndingCommandProperty As DependencyProperty =
DependencyProperty.RegisterAttached("RowEditEndingCommand",
GetType(ICommand),
GetType(DataGridHelper),
New UIPropertyMetadata(AddressOf OnRowEditEnding))
Public Shared Sub SetRowEditEndingCommand(control As DataGrid, command As ICommand)
control.SetValue(RowEditEndingCommandProperty, command)
End Sub
Private Shared Sub OnRowEditEnding(dependencyObject As DependencyObject, e As DependencyPropertyChangedEventArgs)
Dim control As DataGrid = TryCast(dependencyObject, DataGrid)
If control Is Nothing Then
Throw New InvalidOperationException("This behavior can be attached to a DataGrid item only.")
End If
If e.NewValue IsNot Nothing AndAlso e.OldValue Is Nothing Then
AddHandler control.RowEditEnding, AddressOf RowEditEnding
ElseIf e.NewValue Is Nothing AndAlso e.OldValue IsNot Nothing Then
RemoveHandler control.RowEditEnding, AddressOf RowEditEnding
End If
End Sub
Private Shared Sub RowEditEnding(sender As Object, e As DataGridRowEditEndingEventArgs)
Dim element As UIElement = DirectCast(sender, UIElement)
Dim command As ICommand = DirectCast(element.GetValue(DataGridHelper.RowEditEndingCommandProperty), ICommand)
'command.Execute(Nothing)
command.Execute(e)
End Sub
End Class
So far this seems to work, and appears simpler than the approach above. We pass the DataGridRowEditEndingEventArgs back to the Command in the parameter, so it is available in the ViewModel. This will probably also work for the CellEditEnding event.
I need your help to clarify whether form work exactly like object?
If I create an object,obj2, from another object,obj1. the obj2 will be disposed on obj1 dispose.
However it is not so with forms.
check out the case & pseudo code give below.
I have three forms; form1, form2 & form2.
form1 is the startup form.
form1 will create &s how a form2 and from2 will create & show form3 using a button in each form.
if I close form2, after opening all the 3 forms, I am able to work in form1 and form3.
my question is even though form3 is created from form2, why it is not disposed on form2 close?
Form1
Public Class Form1
Private Sub cmdOpenForm2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdOpenForm2.Click
Dim frm As New Form2
With frm
''/.MdiParent = frmMain
.Show()
.BringToFront()
End With
End Sub
Private Sub Form1_Disposed(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Disposed
''//frmMain.tsStatus.Text = "Form1 disposed"
End Sub
End Class
Form2
Public Class Form2
Private Sub cmdRandomColor_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdRandomColor.Click
Randomize()
Label1.ForeColor = Color.FromArgb(Rnd() * 255, Rnd() * 255, Rnd() * 255, Rnd() * 255)
End Sub
Private Sub Form2_Disposed(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Disposed
frmMain.tsStatus.Text = "Form2 disposed"
End Sub
Private Sub cmdOpenForm3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdOpenForm3.Click
Dim frm As New Form3
With frm
''//.MdiParent = frmMain
.Show()
.BringToFront()
End With
End Sub
End Class
Form3
Public Class Form3
Private Sub cmdRandomColor_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdRandomColor.Click
Randomize()
Label1.ForeColor = Color.FromArgb(Rnd() * 255, Rnd() * 255, Rnd() * 255, Rnd() * 255)
End Sub
Private Sub Form3_Disposed(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Disposed
frmMain.tsStatus.Text = "Form3 disposed"
End Sub
End Class
Any Help will be greatly appreciated.
EDIT
thank you all for the solution
Sorry to mention, I am not looking for a solution how to do dispose form3 on form2 close.
my interest is what is happening behind... Is there any possibility that form3 instance created from2 get GC collect and i get a memory error.
since i am getting protect memory access exception in my real application, which is not properly designed, and it too big to refactor now.
my question is where the form3 instance created? Is it in Form2 instance or somewhere else.
since i can able to access form3 instance after form2 disposed. i doubt it is created in somewhere else
There's nothing automatic happening in Dispose calls on forms related to new forms you create yourselves.
If you want a form to automatically dispose of a form it creates, you have to add code yourself to do this. Either find the .Dispose method added by the designer, and add it there, or implement the FormClosed event on the form.
That form2 "creates" form1 does not make any kind of relationship between the two.
Automatic disposal is available as an option. The form must be an "owned" form. The easiest way to do this is to use the Form.Show(owner) overload:
private void button1_Click(object sender, EventArgs e) {
new Form3().Show(this);
}
Or you can do it afterwards with the Form.AddOwnedForm() method. Beware that this has side-effects, an owned form is always shown in front of the owner. And it will get minimized and restored along with the owner. If you don't want this, you can keep explicit track of the lifetime of the form and dispose it yourself:
private Form3 mForm3;
private void button1_Click(object sender, EventArgs e) {
if (mForm3 == null) {
mForm3 = new Form3();
mForm3.FormClosed += (s, ea) => mForm3 = null;
mForm3.Show();
}
else {
mForm3.WindowState = FormWindowState.Normal;
mForm3.Focus();
}
}
protected override void OnFormClosed(FormClosedEventArgs e) {
if (mForm3 != null) mForm3.Dispose();
}
The framework is still holding on to your form object, until it gets closed. You can get a reference to it using My.Application.OpenForms.
If i create an object,obj2, from another object,obj1. the obj2 will be disposed on obj1 dispose.
Not necessarily. If some other object, say obj3 has a reference to obj2, then obj2 will stick around even after obj1 has been garbage collected.
In this case, the window manager has a reference to form3, so it won't be garbage collected even when form2 is.
You have to use the Form-Constructor that takes a Win32-Window-Handle as a parameter. You can pass a reference of form2 to form3 then and it works as you expected.
Private Sub cmdOpenForm2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdOpenForm2.Click
Dim frm As New Form2(Me)
With frm
.Show()
.BringToFront()
End With
End Sub
If you change the form variables to instance variables, and include this in the Show method, it will work
Code for Form1
public partial class Form1 : Form
{
Form2 f;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
f = new Form2();
f.Show(this);
}
private void button2_Click(object sender, EventArgs e)
{
f.Close();
f = null;
}
}
Code for Form2
public partial class Form2 : Form
{
Form3 f;
public Form2()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
f = new Form3();
f.Show(this);
}
}