MAUI: Unable to add an event handler via XAML if the event is internal - xaml

I found an unexpected XAML parser behavior. Here is a page from a MAUI project:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MauiApp1"
x:Class="MauiApp1.MyPage">
<local:MyView MyEvent="MyView_MyEvent" />
</ContentPage>
local:MyView is:
namespace MauiApp1;
class MyView: View
{
public event EventHandler MyEvent;
}
Pay attention to the event's public modifier. This is a fully working project until I change the modifier to internal. The page and MyView are entities of the same assembly, so the modifier change should have no affect on the app. However, it causes the LoadFromXaml method called from MyPage.InitializeComponent to throw a XamlParseException. With the internal modifier, I can still successfully add the event handler via C#, but not via XAML.
I decided to find out if this behavior is inherent in XAML in principle. In fact, this proved not to be the case. For example, WPF doesn't mind adding handlers to internal events via XAML.
Therefore, I would like to know: is this a bug from the MAUI developers or am I missing something?

When changing the modifier to Private, it'll also throw a XamlParseException.And this is a known issue Private EventHandlers throw an exception on XAML-defined custom control in Github, you can follow up it.
Furthermore, I notice you have reported a new issue on Github: https://github.com/dotnet/maui/issues/12566 and you can follow up it.

Related

How to set Page Wide BindingContext in a XAML Page Header

What is the correct syntax for setting the bindingContext of a XAML page, in it's header (Where the namespaces and x:class is defined)?
I know it can be set by
<ContentView.ContextBinding>
<vm:RedViewModel/>
</ContentView.ContextBinding>
but something like the following For Example, would look neater
<?xml version="1.0" encoding="UTF-8" ?>
<ContentView x:Class="MVVMFramework.VVMs.Red.RedView"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:vm="clr-namespace:MVVMFramework.VVMs.Red"
BackgroundColor="Red"
BindingContext="{Binding Source = {vm:RedViewModel}}"> //Something like this
There used to be a bug with the second approach that lead to create the ViewModel twice, not sure if it is there anymore, you could easily check it.
Beside that, there is no silver bullet solution that will work for all cases. What if you have to pass some data to ViewModel constructor? That will be tricky with XAML. Most probably it will make sense to have an IOC container in place, to inject those properties to the ViewModel, so any how it will happen in code and not in XAML.
I would say evaluate yourself what is suitable for your solution and stick to it, so it will be consistent.
P.S.: I am not saying that you should not do it in XAML, do it if it make sense in you specific case.

How do I access resource defined in App.xaml from OnLaunched?

I'm using the MVVM pattern with MVVM Light. I have a ViewModelLocator defined in App.xaml which holds all the ViewModels which are bound to from the Views using Blend.
I have an AppViewModel which basically contains all the important global info for my app, like user preferences. AppViewModel is instantiated by ViewModelLocator, which is in turn, instantiated by app.xaml.
The trouble is, I need to access AppViewModel from inside OnLaunched, however, at this stage of the application lifecycle, it appears app.xaml has not yet instantiated its declared resources. It appears to only do this after a Frame.Navigate().
I need to access AppViewModel because I have to inspect the properties on there in order to work out which Page to load. Hence it's a chicken-and-egg situation.
If I separately instantiate AppViewModel inside OnLaunched, then I can access AppViewModel, but then ViewModelLocator gets instantiated twice which causes problems with my IoC.
Is there a way of manually instantiating items declared inside app.xaml? Or is there a way to get app.xaml to load its resources before presenting a UI?
Or am I doing this wrong? Generally speaking, I regard the application to be the view model layer and the views essentially observe that... so hence I need the ViewModels available before I present any UI.
XAML:
<Application.Resources>
<vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" />
</Application.Resources>
Code:
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
var vml = App.Current.Resources["Locator"] as ViewModelLocator; // exception raised
Exception:
WinRT information: Cannot find a resource with the given key.
thanks
How about you instantiate the ViewModelLocator when you first need it and then add it to Application.Resources once Application.Resources is otherwise initialized or whenever you need? You don't need to add key value pairs to the ResourceDictionaries in XAML always.

Understanding how to map events in xaml

I don't know C# events very well so I have difficulties in understanding some code I found in internet
Inside the code behind of a view file there is the following method:
public void SavingMesBoxClosedHandler(object sender, object args)
The DisplayMessageBox class is derived form FrameworkElement and defines the following event:
public delegate void MessageBoxClosedHandler(object sender, object args);
public event MessageBoxClosedHandler DialogClosed;
Inside the xaml of the view:
<DisplayMessageBoxDemo:DisplayMessageBox
// some dependency properties here
DialogClosed="SavingMesBoxClosedHandler"/>
I thought I could use only dependency properties, while DialogClosed is an event.
Which is the magic to map a method of the view to an event in DisplayMessageBox class using only its name ?
Why don't I have to use a binding ?
Is there an easy way to assign a viewmodel method as the event handler of DialogClose ?
May be I didn't use the correct terms in my last question. To put in other words I want to call a method inside my viewmodel, not in the view as in the example I reported above.
You don't have to use only dependency properties in XAML. You can use normal properties just as well. Dependency properties are a necessity when you use DataBinding, as you point out. In the case you mention there seems to be no need to use DataBinding because you will not use different handlers depending on your DataContext. If you want to stick to the MVVM pattern and keep the event handling logic in your view model, you can use EventTrigger: http://www.kunal-chowdhury.com/2010/11/using-eventtrigger-in-xaml-for-mvvm-no.html

Troubleshooting Win8 Apps: When are my XAML-defined Page fields initialized?

I'm trying the MVVM pattern and I've run into a problem.
Here's how I instantiate my model:
<common:LayoutAwarePage
...
...(omitted boiler plate generated lines here)
...
...
mc:Ignorable="d">
<common:LayoutAwarePage.DataContext>
<local:TextGameClientModel x:Name="textGameClientModel"/>
</common:LayoutAwarePage.DataContext>
But when I try to use it, I get a NullReferenceException because this.textGameClientModel is NULL:
public MainPage()
{
this.InitializeComponent();
this.textGameClientModel.runsPublished += textGameClientModel_runsPublished;
}
I've also tried the same line in the Page's OnNavigateTo handler, and also in the OnLoaded handler, but with the same result.
Where is the right place to hook up my event handler?
(Please don't let my code-behind in an MVVM project distract you from the question. My use of a RichTextBox has forced me to color outside the lines a little.)
I actually wrote an answer about the WPF Creation Steps fairly recently, however that's not the problem in this case.
In this case, you are setting the DataContext in your XAML, however that's not the same as setting the textGameClientModel property
You need to do something like this to set the property equal to your DataContext first
this.textGameClientModel = this.DataContext as GameClientModel;
or simply cast your DataContext as your class to setup the event
((GameClientModel)this.DataContext).runsPublished += textGameClientModel_runsPublished;
As a side note, I never recommend hardcoding the DataContext into a UserControl like you have. By doing so, you are preventing any other DataContext from getting passed to the UserControl, which kind of defeats one of the biggest advantages of WPF/MVVM, which is having separate UI and data layers.

How to correctly inherit from a usercontrol defined in XAML in Silverlight

If I have a usercontrol (in Silverlight) that I've written, that uses XAML to define it's appearance, how can I make a customised version of it?
i.e. I have MyControl.xaml & MyControl.xaml.cs
What do I need to do if I want a "SpecialisedControl" child class? I assume I just make a new code file, then inherit from MyControl. But what if I want to change the appearance of the base class - then what do I do?
I wrote this thinking you were talking about WPF, rather than Silverlight, but there may be enough overlap for this to be helpful, so I'm posting it, anyway.
If by "change the appearance of the base class" you mean "provide a new template", then what you need is probably a CustomControl, not a UserControl.
The best way to accomplish this is to follow the example set by other Microsoft controls, such as Button or ListBox:
Create a class that derives directly from Control (or whatever is closest to your control).
If any properties will need to be exposed to the control (such as text on a button, for example), make sure that you properly define them as DependencyProperties.
As described here, create a ResourceDictionary called Themes/generic.xaml and add a style for your class that includes a template (don't give the style a key).
Use TemplateBindings for any properties of elements on your control that need to get values from your control.
If you'll need to attach any event handlers to elements in your template, give them a unique name. Microsoft uses the convention of prefixing these names with "PART_", and I think it's a good thing to do for the sake of consistency, but it's not strictly required.
Again, if you need to attach event handlers, overload OnApplyTemplate(). In this method, you should detach any old event handlers (we certainly don't want any memory leaks!), and look for elements that have the names your provided in your template--when you find them, attach event handlers, as necessary.
This is certainly much more work than simply deriving from UserControl, but if you want to be able to totally re-template controls, like you can with the built-in controls, this is the way to do it.
On the other hand, if all you want to do is to provide a certain amount of limited customization, such as changing the background, or associating a Command with some user action, then the best thing to do is to expose DependencyProperties, which can then be set in styles for your control, or on instances of your control, itself.
In the case you mentioned of wanting to customize the look in an inherited control, the process is pretty similar: just add a default style for the new control with a new template; if you need to add more event handlers, just be absolutely certain that you call base.OnApplyTemplate().
I dunno, I like doing things with just plain objects. Here's an article that describes an easy way to slip a XAML-designed control outside your inheritance hierarchy so that you can customize appearance and behavior using SimpleThingsLikeInheritance rather than MicrosoftStuffThatAlmostWorks
http://gen5.info/q/2009/02/10/subverting-xaml-how-to-inherit-from-silverlight-user-controls/
As Mihnea's link describes, the easiest solution is to simply add a namespace in your XAML:
C#
public class MyBase : UserControl
{
}
public class FirstUserControl : MyBase
{
...
}
XAML
<local:MyBase
x:Class="FirstUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:local="YourAssembly" ...>
<!-- Sticking with UserControl instead of local:MyBase makes this clearer -->
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
..
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
..Your XAML
</local:MyBase>
You can solve this by using a wrapper as described in the link above.
But you can also use the strategy pattern to solve this problem.
In this post I explain how you implement these two methods.
http://www.lab101.be/2008/07/silverlight-usercontrol-inheritance/