is it possible to edit MainPage.xaml from C# code? - xaml

In my case in need change this
<DrawinSurfaceBackgroundGridgx:Name="DrawingSurfaceBackground"Loaded="DrawingSurfaceBackground_Loaded">
</DrawingSurfaceBackgroundGrid>
to this
<Grid>
<phone:WebBrowser Name="MiniBrowser" Visibility="Collapsed"/>
<!--LayoutRoot is the root grid where all page content is placed-->
<DrawingSurfaceBackgroundGridx:Name="DrawingSurfaceBackground"Loaded=
"DrawingSurfaceBackground_Loaded">
</DrawingSurfaceBackgroundGrid>
</Grid>
When unity3d build default project there is a default MainPage. I need too add a webbrowser component in this mainpage from my unity3d plugin. And then i need to call browser navigate and subscribe to some browser events like loadCompleted Is it possible? Plese give me an example

You can get add/remove items programatically. If you can access the MainPage class you can get to it's content, which is the grid, and to its children which is an UIElementCollection which implements IList.
IList has an insert method which allows you to insert an element at a specified index, in your case 0, here a full example:
MainPage mainPage = new MainPage();
var miniBrowser = new WebBrowser
{
Visibility = Visibility.Collapsed;
};
mainPage.Content.Children.Insert(0, miniBrowser);
I didn't add a name to the WebBrowser class since because it is being added programatically it really makes no difference (the Name property is used at design time (xaml is parsed at design time and at runtime[in the InitializeComponent method]) to generate a property in a partial class (MainPage.i.g.cs/MainPage.g.cs)).
The way you get hold of MainPage is usually from Application.Current.Content (Application.Current will contain a Frame whose Content is MainPage).

Related

Can I define a XAML element in the ViewModel and use it directly in the View using C++/WinRT and WinUI 3?

Many questions have been asked about using a XAML element from the View in the ViewModel. But what about defining a XAML element in the ViewModel and using that in the XAML markup of the View? So that the ViewModel knows about the element from the get-go(and owns the element).
I was trying to do this thinking it would be more MVVM-compliant--the ViewModel doesn't have to know about the entire View in order to use this component. But I recognize it's still not really in the spirit of MVVM to define a XAML element in the ViewModel. But at this point, I just want to know if this technique is possible.
I know I can define a property in the ViewModel, and then bind that to a property of a XAML element defined in the View. E.g. defining a string in the ViewModel and binding that to the Text property of a TextBlock XAML element. But is it possible to simply define the XAML element itself in the ViewModel and use that in the XAML markup of the View?
Example code. I'm using C++/WinRT with WinUI 3:
MainWindowViewModel.idl
namespace MyApp
{
runtimeclass FooViewModel
{
MainWindowViewModel();
Int32 IntProperty; // I can bind this to a XAML element's property
Microsoft.UI.Xaml.Controls.Frame Frame; // Can I use this in the View?
}
}
MainWindow.idl (View)
import "ViewModel/MainWindowViewModel.idl";
namespace MyApp
{
runtimeclass MainWindow : Micorosoft.UI.Xaml.Window
{
MainWindow();
MainWindowViewModel MainWindowViewModel{ get; };
}
}
MainWindow.xaml (View's XAML)
<Window
x:Class="MyApp.MainWindow"
//...
>
<StackPanel>
// I can bind to a property of MainWindowViewModel as such:
<Slider Minimum="0" Maximum="{x:Bind MainWindowViewModel.IntProperty}" />
// Is there a way to use the Frame defined in MainWindowViewModel here, or can you only create a new Frame instance?
<Frame/>
</StackPanel
</Window>
Is it possible to simply define the XAML element itself in the
ViewModel and use that in the XAML markup of the View?
Yes, It will be work.
I tested it in WinUI3 C# and the code works fine.

LoadFromXaml parse exception inflating MenuBarItem

When I try to use the method to inflate my XAML:
MenuBarItem item = new().LoadFromXaml("<MenuBarItem Text=\"Session\"><MenuFlyoutItem Text=\"New\"/><MenuFlyoutItem Text=\"Save\"/><MenuFlyoutItem Text=\"Load\"/></MenuBarItem>");
the MenuBarItem is created and Text properly assigned but all the MenuFlyoutItems are ommited and not added to the menu.
After reading Load XAML at runtime documentation and particularly the "The LoadFromXaml method can be used to inflate any XAML" and the examples given, I assumed that I can throw any valid XAML into it - from a single button, to a DataTemplate of a ListView, a MenuBarItem for a menu, to a whole ContentPage and it should work. But it's not working in this case - I get Microsoft.Maui.Controls.Xaml.XamlParseException and System.Reflection.TargetInvocationException.
Is this behavior a bug or is documentation missing some details about loading XAMLs?
When I enclose the MenuBarItem in a ContentPage's MenuBarItems like this:
new ContentPage().LoadFromXaml("<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<ContentPage\r\n\txmlns=\"http://schemas.microsoft.com/dotnet/2021/maui\"\r\n\txmlns:x=\"http://schemas.microsoft.com/winfx/2009/xaml\"\r\n\tx:Class=\"LoadRuntimeXAML.CatalogItemsPage\"\r\n\tTitle=\"Catalog Items\">\r\n\t<ContentPage.MenuBarItems>\r\n\t\t<MenuBarItem Text=\"Session\">\r\n\t\t\t<MenuFlyoutItem\r\n\t\t\t\tText=\"New\"/>\r\n\t\t\t<MenuFlyoutItem\r\n\t\t\t\tText=\"Save\"/>\r\n\t\t\t<MenuFlyoutItem\r\n\t\t\t\tText=\"Load\"/>\r\n\t\t</MenuBarItem>\r\n\t</ContentPage.MenuBarItems>\r\n</ContentPage>");
it inflates without error and then when I assign elements from the inflated ContentPage to the MainPage's MenuBarItems they display well. But this is an ugly workaround because I don't need a whole ContentPage, just the MenuBarItem.
Your XAML is not complete, thus cannot be parsed.
What the ContentPage has, that your XAML lacks, is the various xmlns lines, that specify the XML elements used in the XAML.
I have not tested, but try replacing <MenuBarItem with
<MenuBarItem\r\n\txmlns=\"http://schemas.microsoft.com/dotnet/2021/maui\"\r\n\txmlns:x=\"http://schemas.microsoft.com/winfx/2009/xaml\"\r\n
Adapt as needed. Any whitespace can be used anywhere \r\n is shown.
If it doesn't work, also prefix with:
<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n
But I believe that is optional.
As an aside, anything that can be done in XAML, can instead be done in C#. C# markup.
C#, being a complete computational language, can often create dynamic UI more easily than XAML, if you are building a UI that depends on different conditions.
A convenient approach in C#, is to define "helper" methods, that take whatever parameters you want, and creates a specific element. That you add to a given parent element, either via C# markup, or methods of a parent layout class.
Its easy to write helper methods that call other helper methods, to build up a whole layout to your specs, controlled at each step by the parameters that matter to you.
At the top level, you might end up with code like this:
// use custom helper methods and methods of "Grid" class.
Grid grid = MyCreateGrid();
grid.Children.Add(MyCreateRowLabel(text), 1, 0);
grid.Children.Add(
// OR use C# markup
new StackLayout
{
Children =
{
new Label().Text("Code:"),
...
}
},
1, 1
);
...
From the official document, it's only using LoadFromXaml for single view or a complete contentPage. I also tried LoadFromXaml for <MenuBarItem Text=\"Session\"><MenuFlyoutItem Text=\"New\"/><MenuFlyoutItem Text=\"Save\"/><MenuFlyoutItem Text=\"Load\"/></MenuBarItem>, and just you said that:
the MenuBarItem is created and Text properly assigned but all the MenuFlyoutItems are ommited and not added to the menu.
But you can achieve it by doing this:
MainPage.xaml:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiApp_loadXaml.MainPage"
x:Name="contentPage">
<Button Text="click" Clicked="Button_Clicked" HeightRequest="50"/>
</ContentPage>
MainPage.xaml.cs:
private void Button_Clicked(object sender, EventArgs e)
{
var xaml = "<MenuBarItem Text=\"Session\"></MenuBarItem>";
var xaml1 = "<MenuFlyoutItem Text=\"New\"/>";
var xaml2 = "<MenuFlyoutItem Text=\"Save\"/>";
var xaml3 = "<MenuFlyoutItem Text=\"Load\"/>";
MenuFlyoutItem menuFlyoutItem_1 = new MenuFlyoutItem().LoadFromXaml(xaml1);
MenuFlyoutItem menuFlyoutItem_2 = new MenuFlyoutItem().LoadFromXaml(xaml2);
MenuFlyoutItem menuFlyoutItem_3 = new MenuFlyoutItem().LoadFromXaml(xaml3);
MenuBarItem item = new MenuBarItem();
item.LoadFromXaml(xaml);
item.Add(menuFlyoutItem_1);
item.Add(menuFlyoutItem_2);
item.Add(menuFlyoutItem_3);
contentPage.MenuBarItems.Add(item);
}
It works well.

How to show the grid on full page in UWP?

I am using syncfusion controls for UWP. One of he feature I have seen in their sample application is showing the data grid on full page.
Something like this
After clicking on this the page looks like this
I am pretty sure that this is not full mode view of the app. After clicking on the button it hides the navigation drawer and the left panel as well.
Can someone please tell how it could be done?
Thanks in advance.
I don't know much about Syncfusion specifically, but I can describe how to implement it in general! This appears to be either a Navigation View or a Master/Details navigation design pattern (I linked both to thier respective documentation pages). Either way, the design pattern typically has the following structure:
Top Frame
Top Page
SplitView (or NavigationView)
Frame
Content Page
Content
Basically, the data grid normally sits on a page. That page is displayed in a frame that is in the content area of a SplitView or NavigationView, which is on the top-level page, which is displayed directly in the top-level frame in the app's window.
The "popping out" effect, that has the data grid display over the navigation drawer is likely some neat trickery going on with manipulating sub-pages, and the frames they are displayed in. So, lets take a look at how this effect can be implemented, given the above info about the structure of the navigation design pattern.
The data grid itself should be placed on its own page, which we will assume is called "DataGridPage" for the following code examples (note that the attributes for the page have been omitted for clarity):
<Page>
<!-- The data grid, or whatever content here -->
</Page>
We are actually adding two layers to the list presented above; the content page has a frame on it, and in that frame is a page with the data grid on it. So, your "content page" in the above list looks like this:
<Page>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition >
<RowDefinition Height="*"></RowDefinition >
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<!-- The bread-crumb, search box, and pop-out button are all in here -->
</Grid>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<!-- The "left panel" here -->
</Grid>
<Frame Name="MyFrame"
Grid.Column="1"></Frame>
</Grid>
</Page>
And then in the constructor for your "content page" (I am assuming C#, but the logic is the same for VB):
public ContentPage()
{
this.InitializeComponent();
// You are telling the frame to display your page with the data grid on it.
// If you don't ever issue a different "Frame.Navigate" statement on this page,
// then it will statically display your data grid as if it was on the same
// page.
this.MyFrame.Navigate(typeof(DataGridPage));
}
Now, I don't know how the hamburger menu is implemented (the "navigation drawer"), and there are several ways to implement one, and I am guessing that it is a control that Syncfucsion provides for you... so I am going to skip trying to write out what the "Top Page" in the above list would look like (although I can detail one solution for implementing that without Syncfusion in a follow-up post if you would like). All that matters here, is that it has, somewhere on it, a frame that displays the "ContentPage" (which as detailed above contains the left panel and a display of your data grid page).
At the top level of the UWP app, the window your app is running in contains a single frame, which is itself displaying the "Top Page". This is important to note, as it is critical to how this "popping out" effect likely works.
On your "Content Page", handle the "Click" event of the "pop-out" button in the upper right of the page. In that event handler, you are going to take your data grid page, and display it in the top frame (the one directly inside of the app window) rather than the "Top Page" (which it is currently displaying).
The mechanics behind this are a little tricky, since you are handling an event on the "Content Page" (the click of the "pop-out" button), but you need to get a reference to the frame the parent page is displayed in (rather than the frame the content page is displayed in).
The solution is to write a custom event. The idea is that you fire this custom event whenever the "pop-out" button is clicked, and then the "top page" responds to the event, asking it's parent frame (the "top frame") to display the data grid page.
Here is a lengthy (and possibly overwhelming) overview of events. But the code you need looks something like the following. On your "Content page":
<!-- Other attributes omitted -->
<Button Name="PopOutButton"
Click="PopOutButton_Click">
And then in the code of the "Content page":
public sealed partial class ContentPage : Page
{
// This is the declaration for your custom event.
public delegate void PopOutRequestedHandler(object sender, PopOutRequestedEventArgs e);
public event PopOutRequestedHandler PopOutRequested;
private void RequestPopOut()
{
// Ensure that something is listening to the event.
if (this.PopOutRequested!= null)
{
// Create the args, and call the listening event handlers.
PopOutRequestedEventArgs args = new PopOutRequestedEventArgs();
this.PopOutRequested(this, args);
}
}
public void PopOutButton_Click(object sender, RoutedEventArgs e)
{
// Any animation logic you want goes here.
this.RequestPopOut();
}
}
You can define a class PopOutRequestedEventArgs to include whatever properties you want to pass in your event. None are strictly necessary for the basic implementation.
Now, on your "Top Page", you need to subscribe to the event. Do so in the "Navigated" event handler for the frame on the top page. So you have this somewhere on your top page:
<Frame Name="NavFrame"
Navigated="NavFrame_Navigated"></Frame>
Then in your "Top page" code, you have:
private void NavFrame_Navigated(object sender, NavigationEventArgs e)
{
if (e.Content is ContentPage page)
{
page.PopOutRequested += this.ContentPage_RequestedPopOut;
}
}
private void ContentPage_RequestedPopOut(object sender, PopOutRequestedEventArgs e)
{
// This tells the frame this page (the "Top Page") is in, to
// instead display the data grid page.
this.Frame.Navigate(typeof(DataGridPage));
}
That is the effect, in a nutshell. To get the page to "close" again, simply put a button on your datagrid page (which you can hide when the page is displayed in the frame on the "Content page"), and handle its Click event. In that event, request the frame navigate back to the "Content page". So, on your data grid page somewhere, you have the same button as detailed above on the "Content page", and in its event handler you put:
this.Frame.Navigate(typeof(ContentPage));
Now, it is important that you ONLY allow the button to be clicked when the "data grid page" is displayed in the "top frame", as that code references the frame the page is displayed in when it is run... it is not an absolute reference. You will get some unexpected results if you call that code while the "data grid page" is displayed in the frame on the "Content page".
As for restoring the state of the "Top page" when you navigate back to it when closing out of the "popped out" view of the data grid, you can accomplish that via the "NavigationEventArgs". Store the state of the "Top page" (such as the fact that the frame on it is displaying the "Content page") in your own class (we will call it "NavArgs" here), and then pass that as a parameter in the Navigate statements:
// Create an instance of the args class, and store your state info.
NavArgs navArgs = new NavArgs();
this.Frame.Navigate(typeof(DataGridPage), navArgs);
You can store the "navArgs" on your data grid page, and then when exiting out of the "popped out" view, and navigating back to the "Top page" in the "top frame", similarly pass them through the Frame.Navigate() call.
It's a little thorough (although still glosses over a lot of concepts), and it is only one possible way to implement that functionality, but hopefully it helps!
You can show the grid in full page by using the property GridColumnSizer.Star .
In GridColumnSizer.Star type, the available grid width is divided into equal sized regions and the divided width is set for each column. When you have three columns in the grid, the total space is divided by three and that value is allocated to the width of the column.
You can get more details about ColumnSizing from the below links.
https://help.syncfusion.com/uwp/sfdatagrid/columns#column-sizing
Regards,
Sathiyathanam

Template10 ModalDialog xaml error - Cannot create an abstract class

I have a UWP application based on the Template10 hamburger template. I added the following modal dialog.
<Controls:ModalDialog x:Name="LoginModal"
CanBackButtonDismiss="False"
DisableBackButtonWhenModal="True">
<Controls:ModalDialog.ModalContent>
<views:LoginView x:Name="loginPart"
HorizontalAlignment="Center"
VerticalAlignment="Center"
HideRequested="LoginHide"
LoggedIn="LoginLoggedIn" />
</Controls:ModalDialog.ModalContent>
</Controls:ModalDialog>
My login view is declared like this
[Export]
public sealed partial class LoginView : Page, IView
{
....
}
For some reason the xaml extract above gives me a design time error saying "Cannot create an abstract class." The project builds and runs ok but the xaml designer won't work.
Anyone know what I'm doing wrong here?
LoginView would need to be a UserControl not a Page. The other properties would be added to that codebehind of that usercontrol to allow for interaction with the ModalDialog
From the Samples folder...
https://github.com/Windows-XAML/Template10/blob/master/Samples/Search/Controls/LoginPart.xaml.cs
if this doesn't work then I would suspect the MEF [Export]

UI Page inside class library

I am developing a class library in Windows 8 (C#), in which i require to show an UI to get user input. How to create UI inside class library and invoking it. Please help.
Now I am invoking the Popup from class library to show the required UI, but I found the popup is opening from library but its hiding below the other UI element (Webview in my case).Please refer the code snippet below.
Class library code
namespace PopUpLibrary
{
public class PopupDialog
{
public void ShowPopup()
{
Popup popup = new Popup();
popup.HorizontalAlignment = HorizontalAlignment.Center;
popup.VerticalAlignment = VerticalAlignment.Center;
popup.Height = 500;
popup.Width = 700;
Button button = new Button();
button.Content = "adfadfad";
button.Width = 200;
button.Height = 100;
popup.Child = button;
popup.IsOpen = true;
}
}
}
Application code:
MainPage.xaml
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<WebView Name="webview" ></WebView>
</Grid>
MainPage.xaml.cs
public MainPage()
{
this.InitializeComponent();
webview.Navigate(new Uri("http://www.google.com"));
PopupDialog popupdialog = new PopupDialog();
popupdialog.ShowPopup();
}
You cannot show other controls on top of the WebView. However, you can work around this by temporarily hiding the WebView and displaying a WebViewBrush.
From the WebView Documentation:
WebView has the characteristic that other UI regions such as controls cannot be rendered on top of the WebView. This is because of how window regions are handled internally, particularly how input events are processed and how the screen draws. If you want to render HTML content and also place other UI elements on top of that HTML content, you should use WebViewBrush as the render area. The WebView still provides the HTML source information, and you reference that WebView through the SourceName property. WebViewBrush does not have this overlay limitation.
If you want to display an interactive WebView that only occasionally has overlapping content (such as a drop-down list or app bar), you can temporarily hide the WebView control when necessary, replacing it with an element using a WebViewBrush fill. Then, when the overlapping content is no longer present, you can display the original WebView again. For more info, see the WebView control sample.
What kind of input? Yes/No or string input?
For Yes/No you could use the standard MessageDialog. If you need to receive custom input, such as text, you could use the CustomDialog control, available in the Callisto toolkit.