I am trying to create a custom Maui Handler for WinUI3 NavigationView.
I have defined the mapping from the virtualView to the PlatformView but i cant seem to convert the Virtual view Content to the PlatformView Content,
An example would be I have a NavigationView.Content = Microsoft.Maui.Frame.Content.
Looking through the maui code there seems to be some mappers but i cant figure out how to get these invoked.
Here is my current code
public static void MapContent(IZtNavigationViewHandler viewHandler, IZtNavigationView virtualView)
{
((NavigationView)(viewHandler?.PlatformView)).Content = virtualView.Content;
}
The mapper is being called but it cant convert the content correctly
Any Ideas or pointers?
CodePudding user response:
TL;DR The lines in a hand-written custom handler’s mapper method, where Content is mapped:
if (handler.VirtualView.PresentedContent is IView view)
handler.PlatformView.Children.Add(view.ToPlatform(handler.MauiContext));
OR if PlatformView has a Content property (NavigationView does), then:
if (handler.VirtualView.PresentedContent is IView view)
handler.PlatformView.Content = view.ToPlatform(handler.MauiContext);
Approach 3 would use that syntax.
First, Frame
, ListView
, and TableView
are "legacy" Renderer-based views. They don't use handlers.
Don't use one of those.
Try ContentView
as the virtual view.
In Maui source, ContentViewHandler.Windows.cs
is the default behavior.
Given:
public class MyContentView : ContentView
{
}
Goal is to make a handler that maps MyContentView to WinUI3's NavigationView, and MyContentView.Content to NavigationView.Content.
ALTERNATIVE 1 - MyContentViewHandler
Similar to Customize specific control instances,
and similar to Maui source solution 'Microsoft.Maui' /
Controls / samples / Maui.Controls.Sample / Controls / BordelessEntry /
BordelessEntryHandler.cs.
One significant difference: Instead of mapping directly to a NavigationView, let MyContentView create ContentPanel (logic inherited from ContentViewHandler), then make NavigationView the child of ContentPanel.
using Microsoft.Maui;
using Microsoft.Maui.Handlers;
...
public class MyContentViewHandler : ContentViewHandler
{
public static IPropertyMapper<IContentView, IContentViewHandler> Mapper =
new PropertyMapper<IContentView, IContentViewHandler>(ViewMapper)
{
[nameof(IContentView.Content)] = MapContent,
};
public static void MapContent(IContentViewHandler handler, IContentView page)
{
UpdateContent(handler);
}
static void UpdateContent(IContentViewHandler handler)
{
_ = handler.PlatformView ?? throw new InvalidOperationException($"{nameof(PlatformView)} should have been set by base class.");
_ = handler.VirtualView ?? throw new InvalidOperationException($"{nameof(VirtualView)} should have been set by base class.");
_ = handler.MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} should have been set by base class.");
handler.PlatformView.Children.Clear();
if (handler.VirtualView.PresentedContent is IView view)
{
// platform equivalent to Content.
var platformContent = view.ToPlatform(handler.MauiContext);
#if WINDOWS
// Wrap content in a NavigationView.
var navView = new NavigationView();
navView.Content = platformContent;
handler.PlatformView.Children.Add(navView);
#else
handler.PlatformView.Children.Add(platformContent);
#endif
}
}
}
ALTERNATIVE 2 - Mapper.AppendToMapping
INCOMPLETE ... not sure how/where to do this ...
Technique: AFTER standard mapping is performed, "fix up" MyContentView.
using Microsoft.UI.Xaml.Controls;
Microsoft.Maui.Handlers.ContentViewHandler.Mapper.AppendToMapping(
"MyContentViewAsNavView", (handler, view) =>
{
if (view is MyContentView)
{
#if WINDOWS
if (handler.PlatformView.Children.Count > 0) {
// ASSUME first child is the Content to wrap.
var platformContent = handler.PlatformView.Children[0];
// Wrap content in a NavigationView.
var navView = new NavigationView();
navView.Content = platformContent;
handler.PlatformView.Children[0] = navView;
}
#endif
}
});
ALTERNATIVE 3 - "NavigationPanel" adapter
(Instead of "nesting" NavigationView inside of ContentPanel.)
- Copy Maui sources ContentViewHandler.cs and ContentViewHandler.Windows.cs, to MyContent... files.
- Replace "ContentView" with "MyContentView" and "ContentPanel" with "NavigationView".
- Observe that it won't compile. "CrossPlatformMeasure" and "CrossPlatformArrange" don't exist in "NavigationView".
- Make a new class "NavigationPanel" to adapt "NavigationView". Equivalent of "ContentPanel" which adapts "Panel" for ContentView. Adapt from
Microsoft.Maui.Platform.ContentPanel
source. - Where you previously replaced "ContentPanel" with "NavigationView", instead use this new "NavigationPanel".
CodePudding user response:
Thanks to ToolmakerSteve I found out a direction to do it in.
The ControlView must inherit from both View and IContentView,
// So the interface would be
[ContentProperty("Content")]
public interface IMyControlView : IContentView
{}
// Then the Control must be constructed with
public partial class MyControlView : View, IContentView, IMyControlView
{
// Then in the view implement the MapContent
public object Content
{
get { return (View)GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
// you must also implement PresentedContent
IView? IContentView.PresentedContent => (View)Content;
}
Then in your handler do the mapping for Content and expose it as
public partial class MyControlViewHandler : ViewHandler<MyControlView, ActualControl>, IMyControlViewHandler
{
public static void MapContent(IZtNavigationViewHandler handler, IZtNavigationView virtualView)
{
//((NavigationView)(viewHandler?.PlatformView)).Content = virtualView.Content;
_ = handler.PlatformView ?? throw new InvalidOperationException($"{nameof(PlatformView)} should have been set by base class.");
_ = handler.VirtualView ?? throw new InvalidOperationException($"{nameof(VirtualView)} should have been set by base class.");
_ = handler.MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} should have been set by base class.");
#if WINDOWS
if (handler.VirtualView.PresentedContent is IView view)
handler.PlatformView.Content = view.ToPlatform(handler.MauiContext);
#endif
}
}
With this i was able to use the WinUI3 NavigationView with Content from ScrollView, BorderView, ContentView.
More to test but thanks for all the help