WPF Control: where is "OnLoaded" virtual function? - wpf

In WinForm's control, there is an OnLoaded virtual function, but this seems to be missing in WPF control. I found this function very useful in some situations. For example, I could do something here after the control is "completely" initialized. In WPF control, there is an OnInitialized virtual function, but this function is called from InitializeComponent function which is too early and it doesn't allow derived class to setup. Is there any reason not to have this function in WPF? Or did I miss anything?

You can attach to the Loaded event of your Window object and do what you want to do inside the event handler (assuming you are using c#):
public MyWindow() //constructor
{
this.Loaded += MyWindow_Loaded;
}
private void MyWindow_Loaded(object sender, RoutedEventArgs e)
{
// do your stuff here
}

you will be looking for FrameworkElement.EndInit()
This will work after the initialization process of the Element...

Related

Windows Phone 8: Moving Event Handlers into the base View class

I have a number of views in my Windows Phone 8 app that share a lot of similarity, so I created a base class to contain the common logic. In general, this approach has worked great, but with one limitation - I am unable to move the common event handling logic into the base class. Here is the simplified version of what I am trying to achieve:
The base class defines a method which my sub-classes should use to handle a button click event in their respective application bars.
public class BaseView : PhoneApplicationPage
{
protected void OnButtonClick(object sender, EventArgs e)
{
MessageBox.Show("Button Pressed");
}
}
The XAML for the inheriting view looks like this (showing only the relevant part). Note the ApplicationBarIconButton's event handler. My intention is that the OnButtonClick method, defined in the base view, is called.
<base:BaseView
x:Class="EventHandlersInBaseClassBugRepro.MainPage"
xmlns:base ="clr-namespace:EventHandlersInBaseClassBugRepro"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
..... >
<phone:PhoneApplicationPage.ApplicationBar>
<shell:ApplicationBar IsMenuEnabled="False">
<shell:ApplicationBarIconButton
IconUri="/Assets/Tiles/FlipCycleTileSmall.png"
Text="Click"
Click="OnButtonClick"/>
</shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>
The application compiles, but at runtime I am getting this exception:
{System.Windows.Markup.XamlParseException: Failed to assign to property 'Microsoft.Phone.Shell.ApplicationBarIconButton.Click'. [Line: 22 Position: 23]
at System.Windows.Application.LoadComponent(Object component, Uri resourceLocator)
at EventHandlersInBaseClassBugRepro.MainPage.InitializeComponent()
at EventHandlersInBaseClassBugRepro.MainPage..ctor()}
I saw a similar question here Silverlight: Can I set an event to a base class event handler in XAML?. My interpretation of the answer suggests that the approach I am using should work, yet the exception.
Is what I am trying to do possible?
Remember Events are a delegate that should only be invoked in the class that declared them.
In your scenario you havent declared an Event in the BaseView.
Also mark the OnButtonClick invoke method as Virtual so that it can be overridden in derived classes. Try
public class BaseView : PhoneApplicationPage
{
public event EventHandler ButtonClicked;
public virtual void OnButtonClick(object sender, EventArgs e)
{
MessageBox.Show("Base Button Click");
}
}
In your derived class .cs you will need to override OnButtonClick like this
public override void OnButtonClicked(object sender, EventArgs e)
{
// do derived class logic here
MessageBox.Show("Derived class hit");
// calls BaseView event invoke method
base.OnButtonClicked(sender, e);
}
Hope that helps

Is there any way to get MainWindow in custom control without using Application class?

I have one custom control which is placed inside the WPF Window,is there any possibility to get that WPF Window in Custom control and hook some events on that Window? without using Application class(ex Application.Current.Mainwindow)
Ahh... how about the Window.GetWindow method?:
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
Window parentWindow = Window.GetWindow(this);
}
Note that it won't work in the constructor, but if you use the Loaded event, it works just fine.

Control Initialization Order Fiasco

Consider the following code:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<Slider ValueChanged="slider_ValueChanged/>
<TextBox x:Name="counter"/>
</StackPanel>
</Window>
and
namespace Project1
{
public partial class Window1 : Window
{
public MainWindow() { InitializeComponent(); }
void slider_ValueChanged(object sender,
RoutedPropertyChangedEventArgs<double> e)
{
counter.Text = e.NewValue.ToString();
}
}
}
Slider will raise its ValueChanged event during initialization while counter is still null.
This is an example of a larger problem that I've been running into using WPF, that UI events can fire at any time, and that there is no single place where I can put my initialization code so that it's guaranteed to run after all the pointers owned by the WPF system have been initialized but before any UI events have fired.
What is the most elegant way to deal with this? The fact that this specific example should use data binding is beside the point.
There are many ways to deal with this, depending on your situation
First off, you could simply recognize the fact that the object might not be initialized and check for that before processing. For example,
if (counter.Text != null)
counter.Text = e.NewValue.ToString();
Second, you could attach your events in the Loaded event of the object so they don't fire until after the object has been initialized.
void Counter_Loaded(object sender, EventArgs e)
{
slider.ValueChanged += Slider_ValueChanged;
}
void Counter_Unloaded(object sender, EventArgs e)
{
slider.ValueChanged -= Slider_ValueChanged;
}
And last of all, you can use WPF's Dispatcher to run events on the UI thread at a different DispatcherPriority. The default is Normal, which runs after Loaded, Render, and DataBind operations
Dispatcher.BeginInvoke(DispatcherPriority.DataBind,
new Action(delegate() { counter.Text = e.NewValue.ToString(); }));
The true answer to this question is to use the MVVM pattern where window code behind files contain little to no initialization code.
In this pattern, the UI is connected to the rest of the code with data binding only. You write special view-model classes that implement INotifyPropertyChanged and take your business logic and expose it as a series of properties that UI binds to.
Naturally, you fully control how your view-models initialize.

WPF XAML Intellisense doesn't work correctly for custom routed event handlers

I defined a custom routed event with the following constructs (names changed):
public class MyRoutedEventArgs : RoutedEventArgs
{...}
public delegate void MyRoutedEventHandler(Object sender, MyRoutedEventArgs args);
public static readonly RoutedEvent MyEvent;
MyClass.MyEvent =
EventManager.RegisterRoutedEvent("MyEvent", RoutingStrategy.Tunnel, typeof(MyRoutedEventHandler), typeof(MyClass));
Next, I'm creating a CLR event wrapper:
public event MyRoutedEventHandler MyEvent {
add { AddHandler(MyEvent, value); }
remove { RemoveHandler(MyEvent, value); }
}
The problem is when I define it as shown above, XAML intellisense doesn't work for autogenerating the handler method body. What I noticed is that if you change your CLR event wrapper to use generic RoutedEventHandler type, everything works! However in this case, the auto-generated method gets a generic RoutedEventArgs (well, correctly corresponding to RoutedEventHandler), which forces me to manually rewrite it as MyRoutedEventArgs.
I think this is currently a limitation you have to live with. But all you have to do is, once the eventhandler for the generic type was autogenerated, change the signature of the generated method to use MyRoutedEventArgs instead of RoutedEventArgs. Allthough still ugly (we shouldn't be forced to do this) I would think it is not such a big problem.
Anyone tried it with VS 2010 ?

In a WPF app, is there a object I can assign to FileSystemWatcher.SynchronizingObject?

In a WPF app, is there a object I can assign to FileSystemWatcher.SynchronizingObject?
I can make my own, but if there is one available, I would like to use it.
Reflector shows that the only class that implements ISynchronizeInvoke (i.e., the type of the FileSystemWatcher.SynchronizingObject property) is System.Windows.Form.Control (and its subclasses); there do not appear to be any WPF objects that implement this interface.
You need to create an ISynchronizeInvoke object that wraps the System.Windows.Threading.Dispatcher instance from the Window. That class is the closest thing WPF has to an ISynchronizeInvoke object.
Inside the wrapper class, simply forward the BeginInvoke call to the dispatcher you've wrapped. I did a bit extra work and also wrapped the DispatcherOperation that results from the Dispatcher.BeginInvoke method to call its Wait method inside the ISynchronizeInvoke.EndInvoke method.
Everything seems to be working correctly so far, it's too bad Microsoft didn't see fit to have the Dispatcher class implement the interface for ease of use.
There is one way. FileSystemWatcher when you enabling events (EnableRaisingEvents = true) creates it's own thread to monitor the FS events. Via ISynchronizeInvoke it can async invoke members of your Form for example, (it's thread can async interact with the main thread - UI thread).
In WPF there's no implementation of ISynchronizeInvoke, but there is a possibility to
interact with the UI thread, via Dispatched property of your Window like this:
var fsw = new FileSystemWatcher()
{
//Setting the properties: Path, Filter, NotifyFilter, etc.
};
fsw.Created += (sender, e) =>
{
Dispatcher.Invoke(new Action<params_types>((params_identifiers) =>
{
//here the code wich interacts with your IU elements
}), here_params);
};
//... in this way (via Dispatcher.Invoke) with the rest of events
fsw.EnableRaisingEvents = true;
Use the DispatcherTimer rather than the system timer.
This will work fine for WPF.
DispatcherTimer t1 = new DispatcherTimer();
private void Window_Loaded(object sender, RoutedEventArgs e)
{
t1.Interval = new TimeSpan(0,0,0,0,200);
t1.Tick += new EventHandler(t1_Tick);
t1.Start();
...
void t1_Tick(object sender, EventArgs e)
{
//some work
}

Resources