I'm developing a WP7 application, and I'm generating a listbox with a few items. I was wondering if there is a way to preview how the layout would look. So far, since the elements don't exist, I can't "preview" them.
Is there some way to feed some dummy data or other methods that would help in previewing xaml layouts ?
First - it helps if you use MVVM, or at least ItemsSource binding + ItemTemplate to display your items. Once you are there - Expression Blend has some great tools for sample data.
You go to Data tab, click Create Sample Data/New Sample Data. It will create a sample data as XAML and bind your page to it like that:
d:DataContext="{d:DesignData SampleData/MainViewModelSampleData.xaml}"
Then you can add new properties, model collections with different data types and it will automatically generate some data you can use in your XAML.
You should provide a designer data.
There are several ways to do it.
One of the simplest, is to provide a DataContext in your XAML declaration for designer to use when rendering your page display.
In Xaml page declaration:
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
d:DataContext="{d:DesignInstance local:DesignerSampleData, IsDesignTimeCreatable=True}"
The sample data class should have the data that your visual elements bind to:
public class DesignerSampleData: INotifyPropertyChanged
{
public DesignerSampleData()
{
_sampleData = "My test string that will display in VS designer for preview";
}
private String _sampleData;
public String SampleData
{
get { return _sampleData; }
set
{
if (value != _sampleData)
{
_sampleData = value;
NotifyPropertyChanged("SampleData");
}
}
}
In xaml bind to SampleData:
<TextBlock Text="{Binding SampleData}" />
Related
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 can I manipulate the View (XAML) from my ViewModel?
For example, I have a detail view for an object. The view have a grid. Depending of the object, the Grid should have different count of rows and cols. The number of cols and rows is set in the object details. Is it possible to do that from the ViewModel or I have to do that in the View .cs?
I open the detail view from a ListView (OnItemSelect):
await Navigation.PushAsync(new AlgoDetailPage(new AlgoDetailViewModel(algo)));
AlgoDetailPage - Here I want to add the cols and rows, depending of the object.
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="NotsanHessen.Views.AlgoDetailPage"
Title="{Binding Algo.Title}">
<StackLayout>
<Grid>
</Grid>
</StackLayout>
</ContentPage>
The AlgoDetailPage.cs:
public partial class AlgoDetailPage : ContentPage
{
AlgoDetailViewModel viewModel;
public AlgoDetailPage(AlgoDetailViewModel viewModel)
{
InitializeComponent();
BindingContext = this.viewModel = viewModel;
}
public AlgoDetailPage()
{
InitializeComponent();
BindingContext = viewModel;
}
}
The ViewModel:
public class AlgoDetailViewModel : BaseViewModel
{
public Algo Algo { get; set; }
public AlgoDetailViewModel(Algo algo = null)
{
this.Algo = algo;
// Rows: algo.Rows
// Cols: algoCols
}
}
you don't manipulate the View from the VM. Instead, the View should use the VM's properties to determine it's layout. In this case, you would add the rows and cols in the View based on the data from the VM.
Technically, you don't have to do that from View itself. If you want to be exactly orthodox you can create the custom control that will handle the binding for you. Personally I wouldn't recommend this level of following some theory, but if you want it it is possible.
Other than that you may try to take a look at CollectionView that will appear in Xamarin 4.0 it may be close to what you have requested (you haven't specified exactly how column width is to be handled). Also there could be some 3rd party components that can handle this.
If it is a complex grid, have two different content views. In your xaml, based on the condition show or hide the content views. In this way, you can keep the code manageable any time.
And if you REALLY necessarily want to access the View from ViewModel, you can Bind View's Load Event to a Command and pass the View itself as a Command Parameter.
I run often into many problems which leads to refactoring my code...
That is why I want to ask for some recommendations.
The problems I'm running into are:
1) Providing data to XAML
Providing simple data to control value instead of using a value converter. For instance I have a color string like "#FF234243" which is stored in a class. The value for the string is provided by a web application so I can only specify it at runtime.
2) UI for every resolution
In the beginnings of my learning I got told that you can create a UI for every possible resolution, which is stupid.
So I've written a ValueConverter which I bind on an element and as ConverterParameter I give a value like '300' which gets calculated for every possible resolution... But this leads to code like this...
<TextBlock
Height={Binding Converter={StaticResource SizeValue}, ConverterParameter='300'}
/>
3) DependencyProperties vs. NotifyProperties(Properties which implement INotifyPropertyChanged) vs. Properties
I have written a control which takes a list of value and converts them into Buttons which are clickable in the UI. So I did it like this I created a variable which I set as DataContext for this specific Control and validate my data with DataContextChanged but my coworker mentioned that for this reason DependencyProperties where introduced. So I created a DependecyProperty which takes the list of items BUT when the property gets a value I have to render the buttons... So I would have to do something like
public List<string> Buttons
{
get { return (List<string>)GetValue(ButtonsProperty); }
set
{
SetValue(ButtonsProperty, value);
RenderButtons();
}
}
// Using a DependencyProperty as the backing store for Buttons. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ButtonsProperty =
DependencyProperty.Register("Buttons", typeof(List<string>), typeof(MainPage), new PropertyMetadata(""));
private void RenderButtons()
{
ButtonBar.Children.Clear();
ButtonBar.ColumnDefinitions.Clear();
if(Buttons != null)
{
int added = 0;
foreach (var item in Buttons)
{
var cd = new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) };
var btn = new Button() { Content = item };
ButtonBar.ColumnDefinitions.Add(cd);
ButtonBar.Children.Add(btn);
Grid.SetColumn(btn, added);
}
}
}
And have to use it like this:
<Controls:MyControl
x:Name="ButtonBar" Button="{Binding MyButtons}">
</Controls:MyControl>
Since these are a lot of topics I could seperate those but I think that this is a pretty common topic for beginners and I have not found a got explanation or anything else
1. Providing data to XAML
There are two options: prepare data in the ViewModel or to use converter.
To my mind using converter is better since you can have crossplatform viewModel with color like you mentioned in your example and converter will create platform dependent color. We had similar problem with image. On android it should be converted to Bitmap class, while on UWP it's converted to BitmapImage class. In the viewModel we have byte[].
2. UI for every resolution
You don't need to use converter, since Height is specified in effective pixels which will suit all the required resolutions automatically for you. More info can be found at the following link:
https://learn.microsoft.com/en-us/windows/uwp/design/layout/layouts-with-xaml
There are two options how to deal with textblock sizes:
a) Use predefined textblock styles and don't invent the wheel (which is the recommended option):
https://learn.microsoft.com/en-us/windows/uwp/design/style/typography#type-ramp
Or
b) Specify font size in pixels. They are not pixels, but effective pixels. They will be automatically scaled on different devices:
https://learn.microsoft.com/en-us/windows/uwp/design/style/typography#size-and-scaling
Furthermore, use adaptive layout to have different Layout for different screen sizes.
3) DependencyProperties vs. NotifyProperties(Properties which implement INotifyPropertyChanged) vs. Properties
As per your code you can try to use ListView or ItemsControl and define custom item template.
DependencyProperties are created in DependencyObject and are accessible in xaml. All controls are inherited from DependencyObjects. Usually you create them when you want to set them in xaml. They are not stored directly in the objects, but in the global dictionary and resolved at runtime.
DependencyProperties were created long time ago and you can find lots of links which explain them in details:
http://www.wpftutorial.net/dependencyproperties.html
https://techpunch.wordpress.com/2008/09/25/wpf-wf-what-is-a-dependency-property/
When should I use dependency properties in WPF?
What is a dependency property? What is its use?
What is a dependency property?
INotifyPropertyChanged INPC are the central part of MVVM. You bind your view to viewModel which implements INPC and when you change value of the property control is notified and rereads the new value.
Download the following video in high resolution which explains MVVM in details (by Laurent Bugnion):
https://channel9.msdn.com/Events/MIX/MIX11/OPN03
MVVM: Tutorial from start to finish?
Normal properties are used in model classes or when there is no need to notify UI regarding changes.
I have a control that consists of a button and a textbox.
I wanted to set the input scope of the textbox, so I introduced a new dependency property:
public InputScope InputScope
{
get { return (InputScope)GetValue(InputScopeProperty); }
set { SetValue(InputScopeProperty, value); } // Notify prop change
}
public static readonly DependencyProperty InputScopeProperty =
DependencyProperty.Register(nameof(InputScope), typeof(InputScope), typeof(SearchControl), new PropertyMetadata(DependencyProperty.UnsetValue));
In XAML:
<controls:SearchControl InputScope="Number" /> <!-- etc... -->
(Obviously assigning it to the InputScope property of the textbox in the style of this custom control.)
My problem: While this works, the numeric keyboard gets shown when focused, but I have blue underline in the XAML, and also an error message: The TypeConverter for "InputScope" does not support converting from a string.
Is there a way to fix it without a dirty hack?
Is there a way to fix it without a dirty hack?
You could implement a type converter. Please refer to Tim Heuer's blog post for more information and an example:
Implementing a type converter in UWP XAML: http://timheuer.com/blog/archive/2017/02/15/implement-type-converter-uwp-winrt-windows-10-xaml.aspx
You may also want to read this:
WinRT Replacement for System.ComponentModel.TypeConverter
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).