I have a problem with my behavior using Microsoft Expression Blend. I don't know how to get the OnAttached event in Expression Blend or Visual Studio. It doesn't fire. Here's an example:
public class MyBehavior : Behavior<Path>
{
public PathNavigation()
{
}
protected override void OnAttached()
{
// Only firing in runtime
base.OnAttached();
AssociatedObject.Loaded += AssociatedObject_Loaded;
}
private void AssociatedObject_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
// Only firing in runtime
}
}
Is there a possibility to get that events while designing in Blend?
Thanks in advance.
Chris
Is it possible that at design time, AssociatedObject.IsLoaded == true? You could check that before attaching your event handler, and call a common OnLoaded method.
Related
I'm new to WPF, and trying to fix a bug with a draggable window.
I'm checking a bug in an app I maintain where we have a Behavior class, with which we implement a dragging mechanism.
When I go to windows' display settings and change the scaling (100%/125%/150%/175%), windows seems to dispose the app's window and recreate it.
This is where the AssociatedObject member of the behavior class becomes null, so when the Loaded event is invoked again, I have no AssociatedObject to work with.
How can I get the new AssociatedObject?
It seems like expected behavior since the UI element was recreated, but the behavior class is still 'alive' but can't do any work since it isn't familiar with the new UI element (If I understand things correctly, as I mentioned, I'm new to WPF)
Thanks!
Edit: adding a sample class
public class SetWindowSizeBehavior : Behavior<FrameworkElement>
{
protected override void OnDetaching()
{
Console.WriteLine("detaching");
base.OnDetaching();
}
protected override void OnAttached()
{
AssociatedObject.Unloaded += AssociatedObject_Unloaded;
AssociatedObject.Loaded += AssociatedObject_Loaded;
base.OnAttached();
}
private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
{
Console.WriteLine("");
var a = AssociatedObject;
}
private void AssociatedObject_Unloaded(object sender, RoutedEventArgs e)
{
Detach();
}
}
After I'm changing the scaling settings at the windows display settings menu, this is the call stack:
AssociatedObject_Unloaded
OnDetaching
AssociatedObject_Loaded <- this is where AssociatedObject becomes null
I hope the question is more clear now, and if not, would love to get some comments about missing info...
Can you try overriding the 'OnPropertyChanged' method
public class SetWindowSizeBehavior : Behavior<FrameworkElement>
{
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (AssociatedObject == null)
{
// so, let'save the value and then reuse it when OnAttached() called
_value = e.NewValue as string;
return;
}
if (e.Property == PasswordProperty)
{
if (!_skipUpdate)
{
_skipUpdate = true;
AssociatedObject.Password = e.NewValue as string;
_skipUpdate = false;
}
}
}
protected override void OnDetaching()
{
Console.WriteLine("detaching");
base.OnDetaching();
}
protected override void OnAttached()
{
AssociatedObject.Unloaded += AssociatedObject_Unloaded;
AssociatedObject.Loaded += AssociatedObject_Loaded;
base.OnAttached();
}
private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
{
Console.WriteLine("");
var a = AssociatedObject;
}
private void AssociatedObject_Unloaded(object sender, RoutedEventArgs e)
{
Detach();
}
}
From here:
https://stackoverflow.com/a/42312799/7779827
Eventually I used #DanielFr's idea and when I detected that the associatedObject is null (cause the base class Behavior sets it to null when the detach method is called) I used the sender object and:
if (AssociatedObject == null)
{
//If we got here then we didn't go through onAttached in this iteration
Attach((FrameworkElement)sender);
}
When FrameworkElement is my T in my Behavior class
When my app is in the background and goes to foreground, the OnAppearing() method doesnt work in IOS or when the phone is locked and then unlocked and the app is in the foreground the method OnAppearing() isnt called, on Android everything works fine.
I found this guide below, but still doesnt work, i have the last version of xamarin forms.
Guide:
https://kent-boogaart.com/blog/hacking-xamarin.forms-page.appearing-for-ios
Can anyone help me?
As you have seen, the "lifecycles" within iOS are different. One way that helps is to use the Application lifecycles and tie those into Page events (or Commands if needed).
In your Application subclass add a couple of public EventHandlers and tie those into the OnResume (and OnSleep if needed)
public partial class App : Application
{
public EventHandler OnResumeHandler;
public EventHandler OnSleepHandler;
public App()
{
InitializeComponent();
MainPage = new MyPage();
}
protected override void OnSleep()
{
OnSleepHandler?.Invoke(null, new EventArgs());
}
protected override void OnResume()
{
OnResumeHandler?.Invoke(null, new EventArgs());
}
}
Now in your ContentPage subclass, add a handler that tracks when that page comes back from being in the background, kind-of an "OnAppearing after OnPause" handler...
void Handle_OnResumeHandler(object sender, EventArgs e)
{
Console.WriteLine("OnPauseResumeWithPage");
}
protected override void OnAppearing()
{
(App.Current as App).OnResumeHandler += Handle_OnResumeHandler;
base.OnAppearing();
}
protected override void OnDisappearing()
{
(App.Current as App).OnResumeHandler -= Handle_OnResumeHandler;
base.OnDisappearing();
}
I was looking for a way to stop MouseDown event fired by an ObjectListView from propagation. Almost all suggestions in the net sad: use event.Handled = true;
However, Handled property is not there. I tried Cancel method but is not there too.
Basically, I have an ObjectListView defined as:
public class ObjectListView : Control
{
//...
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
//...
}
}
and I used this object in one of my forms to catch mouse click event in the ObjectListView. So I added an event handler in the form:
partial class Form1
{
private void InitializeComponent()
{
//...
this.ObjectListView.MouseDown +=
new System.Windows.Forms.MouseEventHandler(this.ObjectListView_MouseDown);
}
}
and defined within Form1 a delegate:
public partial class Form1
{
private void ObjectListView_MouseDown(object sender, MouseEventArgs e)
{
// Stop event propagation
}
}
So when I click on the ObjectListView in Form1, the Form1.ObjectListView_MouseDown method was called first and after that ObjectListView.OnMouseDown method.
I'm using .Net Framework 4.
I have written on event in a user control and that user control used twice in a page. Now the problem is, I am getting the Event as null for the 2nd time. Why? How to resolve the issue? Please help.
My code like:
in ascx:
public delegate void OnGoButtonClick();
public event OnGoButtonClick btnGoClickHandler;
protected void btnGo_Click(object sender, EventArgs e)
{
if (btnGoClickHandler != null)
btnGoClickHandler();
}
In aspx:
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
MyUserControl.btnGoClickHandler += new UserControls.LoanPicker.OnGoButtonClick(PopulateDataOnGo);
}
But for the 2nd user control it is always null.
Make sure to subscribe to the event from both controls.
Regarding your comment:
how to detect which user control being triggered
You need to supply the object that raised the event to the event handler. Start by altering the delegate signature to look like this:
public delegate void OnGoButtonClick(object sender);
public event OnGoButtonClick btnGoClickHandler;
protected void btnGo_Click(object sender, EventArgs e)
{
if (btnGoClickHandler != null)
btnGoClickHandler(this);
}
Now alter the event handler which in this case appears to be PopulateDataOnGo to accept the sender and check who raised the event from there:
public void PopulateDataOnGo(object sender)
{
if (sender is ControlType1)
{
}
else if (sender is ControlType2)
{
}
}
In my application I was using SystemEvents to add objects to an ObservableCollection (code shortened for this example)
public partial class App : Application
{
private ObservableCollection<StateChanged> _messages = new ObservableCollection<StateChanged>();
public ObservableCollection<StateChanged> messages { get { return _messages; } }
protected override void OnStartup(StartupEventArgs e)
{
SystemEvents.SessionSwitch += SystemEvents_SessionSwitch;
}
private void SystemEvents_SessionSwitch(object sender, SessionSwitchEventArgs e)
{
messages.Insert(0, new StateChanged(DateTime.Now, State.Logoff));
}
}
Above code works without a problem.
Because I do not only have to handle SessionSwitch events, but also SessionEnding etc. I wrote a small class that should raise a 'unified' event for some of the SystemEvents (again shortened)
public class SystemEventArgs : EventArgs
{
public State newState { get; set; }
}
public delegate void SystemEventHandler(object sender, SystemEventArgs e);
class SystemEventCollector
{
public event SystemEventHandler SessionEvent;
protected virtual void RaiseSystemEvent(SystemEventArgs e)
{
SystemEventHandler handler = this.SessionEvent;
if (handler != null)
handler(this, e);
}
public SystemEventCollector()
{
SystemEvents.SessionSwitch += SystemEvents_SessionSwitch;
}
protected void SystemEvents_SessionSwitch(object sender, SessionSwitchEventArgs e)
{
SystemEventArgs ea = new SystemEventArgs();
ea.newState = State.Unknown;
RaiseSystemEvent(ea);
}
}
When I instanciate this class in my Application and subscribe to the SessionEvent, doing the same stuff, like this
public partial class App : Application
{
private ObservableCollection<StateChanged> _messages = new ObservableCollection<StateChanged>();
public ObservableCollection<StateChanged> messages { get { return _messages; } }
private SystemEventCollector _sysEventCollector = new SystemEventCollector();
protected override void OnStartup(StartupEventArgs e)
{
_sysEventCollector.SessionEvent += OnSessionEvent;
}
private void OnSessionEvent(object sender, SystemEventArgs e)
{
messages.Insert(0, new StateChanged(DateTime.Now, e.newState));
}
}
The messages.Insert() call raises an exception
This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.
I do understand that I can not update a GUI element from another thread than the one it was created on and have worked around this problem by using the extension method mentioned in this SO answer.
My question is to why this happens? My assumptions are that events are handled on the same thread as they are raised, so why is there a difference between handling the SessionSwitch event directly and my approach of raising an event when handling the SessionSwitch event? How are the SystemEvents different from my event? Do I have the correct solution to the problem? Is there a simpler solution?
From some testing it seems that the error lies in the non-working code is the instanciation of SystemEventCollector object.
MS does all the necessary marshalling in their SessionEvents.*** handlers, this is why the first example works without problems. In the non-working code SystemEventCollector is no instanciated in the OnStartup function (which is called from the UI thread) but basically with the constructor. When marshalling from the SessionEvents is done, it goes to the wrong thread, leading to the problem.
Apart from my original solution, the problem can also be solved by instanciating the SystemEventCollector in the OnStartup function.
protected override void OnStartup(StartupEventArgs e)
{
_sysEventCollector = new SystemEventCollector();
_sysEventCollector.SessionEvent += OnSessionEvent;
}